Перейти к основному содержимому

5.16. Типы данных

Разработчику Архитектору

Типы данных

COBOL — один из самых ранних языков программирования высокого уровня, разработанный в конце 1950-х годов с целью упрощения обработки бизнес-данных. В отличие от многих современных языков, где типизация часто строится на абстрактных конструкциях и объектах, COBOL опирается на четкую, декларативную модель описания данных, ориентированную на их внешний вид и поведение в контексте документооборота, бухгалтерии, логистики и других прикладных областей. Основой этой модели служит понятие типа данных, реализованного через механизм PICTURE (PIC) и структуру групповых записей.


PICTURE (PIC) — описание формата данных

В COBOL тип данных не задается ключевым словом вроде int или string. Вместо этого используется спецификация PICTURE, сокращённо PIC, которая описывает формат поля: сколько символов оно содержит, какие символы допустимы, присутствует ли знак, десятичная точка, валютный символ и так далее. Эта спецификация позволяет точно определить, как данные будут храниться, отображаться и обрабатываться.

Каждое поле данных в COBOL объявляется в разделе DATA DIVISION, внутри секции WORKING-STORAGE SECTION или FILE SECTION, и сопровождается PIC-предложением. PIC-строка состоит из специальных символов, каждый из которых имеет строго определённое значение:

  • 9 — цифра от 0 до 9. Используется для числовых данных.
  • X — любой печатаемый символ (буква, цифра, знак препинания, пробел).
  • A — только алфавитный символ (буква).
  • S — указывает наличие знака «плюс» или «минус» перед числом.
  • V — неявная десятичная точка. Она не занимает места в памяти, но указывает, где находится дробная часть.
  • P — используется для масштабирования числа, сдвигая десятичную точку влево или вправо без изменения количества цифр.
  • Z — используется в редактируемых числовых полях для подавления незначащих нулей слева.
  • * — заменяет незначащие нули звёздочками (часто для защиты конфиденциальных данных).
  • B — вставляет пробел вместо нуля.
  • , и . — используются в редактируемых полях для явного отображения разделителей тысяч и десятичной точки.
  • $ — символ валюты, отображается в начале числа.

Эти символы могут комбинироваться и повторяться с помощью круглых скобок. Например, 9(5) эквивалентно 99999, а X(10) — это десять произвольных символов.

Числовые типы данных

Числовые данные в COBOL делятся на несколько категорий в зависимости от представления и использования.

  • Целые числа:
    Пример: PIC 9(5) — целое число, занимающее пять позиций. Максимальное значение — 99999. Такое поле может использоваться для хранения, например, количества единиц товара на складе.

  • Числа со знаком:
    Пример: PIC S9(4) — четырёхзначное целое число со знаком. Знак может быть отдельным символом или закодирован в последней цифре (в зависимости от компилятора и параметров). Такие поля подходят для балансов, температур, финансовых показателей, которые могут быть отрицательными.

  • Десятичные числа с фиксированной точкой:
    Пример: PIC 9(3)V99 — число из трёх целых цифр и двух дробных. Фактически хранится как пять цифр, но программа интерпретирует его как, например, 123.45. Это стандартный способ хранения денежных сумм, весов, процентов и других величин, требующих точности.

  • Масштабированные числа:
    Пример: PIC PPP999 — число, умноженное на 10⁻³. Это означает, что значение 123 будет интерпретировано как 0.123. Такой подход позволяет работать с очень малыми числами без использования явной десятичной точки.

  • Редактируемые числовые поля:
    Пример: PIC $$$,$$9.99 — формат для отображения суммы с разделителями тысяч и двумя знаками после запятой. При выводе значение 123456 будет представлено как $1,234.56. Эти поля предназначены исключительно для отображения, а не для вычислений.

Символьные типы данных

Символьные данные в COBOL описываются с помощью символов X и A.

  • Алфавитно-цифровые строки:
    Пример: PIC X(20) — строка длиной 20 символов, содержащая любые печатаемые символы. Это самый распространённый тип для хранения имён, адресов, описаний, кодов и другой текстовой информации.

  • Алфавитные строки:
    Пример: PIC A(15) — строка из 15 букв. Такие поля гарантируют, что в них не попадут цифры или знаки препинания. Они применяются, когда требуется строгая проверка содержимого, например, для фамилий в некоторых системах.

Символьные поля всегда имеют фиксированную длину. Если строка короче объявленной длины, она дополняется пробелами справа. Это свойство важно учитывать при сравнении и обработке.

Редактируемые и не редактируемые поля

COBOL различает не редактируемые (computational) и редактируемые (edited) поля. Первые предназначены для хранения и вычислений, вторые — для вывода. Переход между ними осуществляется с помощью операторов MOVE и COMPUTE, где значения автоматически преобразуются в соответствии с PIC-спецификацией.

Например, внутреннее поле AMOUNT PIC 9(5)V99 может содержать значение 1234567, которое интерпретируется как 12345.67. При копировании в редактируемое поле DISPLAY-AMOUNT PIC ZZZ,ZZ9.99, результат будет отображён как 12,345.67.


Групповые записи (GROUP ITEM)

COBOL поддерживает иерархическую структуру данных. Поля могут объединяться в групповые записи — составные элементы, которые сами по себе не содержат данных, но служат контейнерами для других полей. Это позволяет моделировать сложные бизнес-объекты, такие как клиент, заказ, счет или транзакция.

Групповая запись объявляется с уровнем (level number), обычно начинающимся с 01. Вложенные поля получают более высокие номера уровней, например 05 или 10. Уровень 01 обозначает корневой элемент записи. Уровни 66 и 88 имеют специальное назначение (для переименования и условных значений соответственно) и не используются для группировки.

Пример:

01 CUSTOMER-RECORD.
05 CUSTOMER-ID PIC 9(6).
05 CUSTOMER-NAME.
10 FIRST-NAME PIC X(20).
10 LAST-NAME PIC X(25).
05 ADDRESS PIC X(50).
05 BALANCE PIC S9(7)V99.

В этом примере CUSTOMER-RECORD — групповая запись. Она включает простые поля (CUSTOMER-ID, ADDRESS, BALANCE) и вложенную группу CUSTOMER-NAME, которая, в свою очередь, содержит FIRST-NAME и LAST-NAME.

Групповые записи позволяют:

  • Обращаться к целому блоку данных одним именем. Например, можно скопировать всю запись CUSTOMER-RECORD в другую переменную того же формата.
  • Читать и записывать структурированные данные из файлов, где каждая строка соответствует одной групповой записи.
  • Упрощать логику программы за счёт логической группировки связанных полей.

При работе с файлами, особенно в мэйнфреймовых системах, групповые записи часто соответствуют фиксированным строкам входных или выходных данных. Это делает COBOL особенно эффективным для обработки больших массивов однородных записей — например, банковских транзакций или страховых полисов.


Особенности типизации в COBOL

Типизация в COBOL является статической и строгой. Все данные должны быть объявлены заранее, и их формат фиксирован на этапе компиляции. Это обеспечивает предсказуемость и надёжность, особенно в критически важных системах, где ошибки могут привести к финансовым потерям.

Однако COBOL допускает неявные преобразования при операциях MOVE и COMPUTE, если форматы совместимы. Например, можно переместить значение из PIC 9(3) в PIC 9(5), и оно будет дополнено нулями слева. Но попытка переместить символьную строку в числовое поле вызовет ошибку времени выполнения или некорректный результат, в зависимости от настроек компилятора.

Для повышения безопасности и читаемости рекомендуется использовать явные преобразования и проверки, особенно при работе с внешними данными.


Уровни 66 и 88 — специальные конструкции для работы с данными

В дополнение к стандартным уровням (01, 05, 10 и так далее), COBOL предоставляет два особых уровня: 66 и 88. Они не описывают физические данные, но играют важную роль в логике программы и управлении значениями.

Уровень 66 — переименование (RENAMES)

Уровень 66 позволяет создать альтернативное имя для части или целого блока данных. Это особенно полезно, когда один и тот же участок памяти должен интерпретироваться по-разному в зависимости от контекста.

Пример:

01 TRANSACTION-RECORD.
05 TX-DATE PIC X(8).
05 TX-AMOUNT PIC 9(7)V99.
05 TX-STATUS PIC X(1).

66 TX-HEADER RENAMES TX-DATE THRU TX-AMOUNT.

Здесь TX-HEADER — это псевдоним, объединяющий поля TX-DATE и TX-AMOUNT. При необходимости можно скопировать или обработать оба поля как единый блок, не перечисляя их по отдельности. Переименование не создаёт новой памяти — оно лишь предоставляет другое представление существующих данных.

Такой подход упрощает работу с переменными частями записей, особенно в устаревших системах, где форматы данных могут меняться в зависимости от типа транзакции.

Уровень 88 — условные имена (Condition Names)

Уровень 88 определяет логические условия, связанные с конкретным значением поля. Это мощный инструмент для повышения читаемости кода и избежания «магических значений».

Пример:

01 ACCOUNT-STATUS    PIC X.
88 ACTIVE VALUE 'A'.
88 CLOSED VALUE 'C'.
88 SUSPENDED VALUE 'S'.

В этом случае поле ACCOUNT-STATUS может принимать значения 'A', 'C' или 'S'. Вместо того чтобы писать:

IF ACCOUNT-STATUS = 'A' THEN ...

можно использовать:

IF ACTIVE THEN ...

Это делает программу более понятной, особенно для аналитиков и бизнес-пользователей, которые могут читать COBOL-код без глубоких технических знаний. Уровень 88 также поддерживает множественные значения:

88 VALID-CODES      VALUE '01', '02', '03', '99'.

и диапазоны:

88 HIGH-BALANCE     VALUE 10000 THRU 999999.

Такие конструкции позволяют выразить бизнес-правила прямо в структуре данных, а не только в логике выполнения.


Редактируемые поля и форматированный вывод

Редактируемые поля в COBOL — это особый класс данных, предназначенный исключительно для отображения информации в человекочитаемом виде. Они не подходят для арифметических операций, но идеальны для генерации отчётов, счетов, накладных и других документов.

Основные символы редактирования:

  • Z — подавляет незначащие нули слева, заменяя их пробелами.
  • * — заменяет незначащие нули звёздочками (часто используется в финансовых отчётах для защиты конфиденциальности).
  • B — вставляет пробел вместо нуля.
  • $ — отображает символ валюты.
  • , — разделяет тысячи.
  • . — десятичная точка.
  • CR/DB — указывает кредит или дебет (в некоторых реализациях).

Пример:

01 SALARY-DISPLAY    PIC $$$,$$9.99.
01 SECRET-AMOUNT PIC ***,**9.99.

Если внутреннее значение равно 1234567 (интерпретируется как 12345.67), то:

  • SALARY-DISPLAY покажет $12,345.67
  • SECRET-AMOUNT покажет **12,345.67

Редактируемые поля автоматически преобразуются при операции MOVE из числовых источников. Это позволяет отделить логику вычислений от логики представления.


Практические примеры использования типов данных

Пример 1: Банковская транзакция

01 BANK-TRANSACTION.
05 TX-ID PIC X(12).
05 TX-DATE PIC 9(8). *> ГГГГММДД
05 TX-AMOUNT PIC S9(9)V99.
05 TX-CURRENCY PIC X(3).
05 TX-DESCRIPTION PIC X(50).

88 VALID-CURRENCY VALUE 'USD', 'EUR', 'RUB'.
88 LARGE-AMOUNT VALUE 100000 THRU 99999999999.

Эта структура позволяет хранить все необходимые данные о переводе, проверять валюту и выявлять крупные суммы для дополнительной верификации.

Пример 2: Отчёт о продажах

01 SALES-REPORT-LINE.
05 PRODUCT-CODE PIC X(10).
05 QUANTITY PIC 9(5).
05 UNIT-PRICE PIC 9(5)V99.
05 TOTAL-AMOUNT PIC 9(7)V99.

01 DISPLAY-LINE.
05 FILLER PIC X(12) VALUE SPACES.
05 DISP-QTY PIC ZZZ,ZZ9.
05 FILLER PIC X(2) VALUE SPACES.
05 DISP-PRICE PIC $$$,$$9.99.
05 FILLER PIC X(2) VALUE SPACES.
05 DISP-TOTAL PIC $$$,$$$,$$9.99.

После вычисления TOTAL-AMOUNT = QUANTITY * UNIT-PRICE, значения копируются в DISPLAY-LINE для печати. Результат будет выглядеть как:

            1,234   $123.45    $152,337.30

Такой подход обеспечивает чёткое разделение между данными и их представлением — принцип, который остаётся актуальным даже в современных архитектурах.


Совместимость и переносимость

COBOL стандартизирован ANSI и ISO, но разные компиляторы (IBM Enterprise COBOL, Micro Focus, GnuCOBOL и другие) могут по-разному интерпретировать некоторые детали, особенно в области редактируемых полей и представления знака. Например, положение знака в числах (S9(4)) может быть:

  • Отдельным символом перед числом (+1234)
  • Закодированным в последней цифре (так называемый trailing sign overpunch)

Поэтому при переносе кода между платформами требуется внимательная проверка форматов ввода-вывода.

Тем не менее, основной механизм PIC и групповых записей остаётся стабильным и совместимым на протяжении десятилетий. Это одна из причин, почему COBOL-системы продолжают работать без изменений с 1970-х годов.


Внутреннее представление данных в COBOL

Хотя COBOL абстрагирует программиста от деталей памяти через PIC-спецификации, знание того, как данные физически хранятся, важно для оптимизации, отладки и интеграции с другими системами.

DISPLAY — символьное представление

По умолчанию все поля в COBOL используют формат DISPLAY. Это означает, что каждая цифра или символ хранится как отдельный байт в кодировке EBCDIC (на мэйнфреймах) или ASCII (на других платформах). Например, число 12345 в поле PIC 9(5) занимает 5 байт, по одному на цифру.

Этот формат удобен для отладки и обмена данными с внешними системами, но неэффективен с точки зрения памяти и скорости вычислений.

COMPUTATIONAL (COMP) — двоичное представление

Для повышения производительности COBOL поддерживает компактные числовые форматы через использование USAGE IS COMPUTATIONAL (или сокращённо COMP).

  • COMP-1 — одинарная точность с плавающей запятой (4 байта, IEEE 754).
  • COMP-2 — двойная точность с плавающей запятой (8 байт).
  • COMP-3 — упакованный десятичный формат (packed decimal), где две цифры хранятся в одном байте, а знак — в последней тетраде. Например, число 12345 в PIC 9(5) COMP-3 займёт всего 3 байта: 12 34 5C (где C — код знака «плюс»).

Формат COMP-3 особенно распространён в финансовых системах, так как он сохраняет точность десятичных дробей без ошибок округления, присущих двоичным числам с плавающей запятой.

Пример объявления:

01 TOTAL-AMOUNT    PIC S9(9)V99 USAGE COMP-3.

Такое поле займёт 6 байт и будет эффективно использоваться в арифметических операциях.

COMPUTATIONAL-4 (COMP-4) — двоичное целое

На некоторых платформах (например, IBM z/OS) используется COMP-4, где числа хранятся в двоичном виде, как в большинстве современных языков. Поле PIC S9(9) COMP-4 займёт 4 байта и позволит быстро выполнять сложение, умножение и другие операции.

Выбор формата зависит от требований к производительности, совместимости и точности. Важно помнить: COMP-поля нельзя напрямую использовать в операциях ввода-вывода — они должны быть преобразованы в DISPLAY-формат перед записью в файл или выводом на экран.


Работа с файлами и соответствие структуре записи

В COBOL данные часто читаются из и записываются в файлы с фиксированной или переменной длиной. Структура групповой записи должна точно соответствовать формату строки в файле.

Например, если файл содержит строки по 100 байт, где первые 10 байт — идентификатор, следующие 30 — имя, и последние 60 — описание, то запись будет выглядеть так:

01 CUSTOMER-FILE-RECORD.
05 CUST-ID PIC X(10).
05 CUST-NAME PIC X(30).
05 CUST-DESC PIC X(60).

При чтении файла с помощью READ вся строка автоматически разбивается по полям в соответствии с этой структурой. Это делает COBOL исключительно удобным для обработки legacy-данных, таких как плоские файлы, ленточные архивы или выгрузки из старых баз данных.

Если в файле используются упакованные числа (COMP-3), то соответствующие поля в записи также должны быть объявлены с USAGE COMP-3. Иначе произойдёт искажение данных.


Современное значение типов данных COBOL

Несмотря на возраст языка, его модель типов остаётся актуальной в контексте:

  • Миграции legacy-систем: при переносе COBOL-приложений в облако или на микросервисную архитектуру важно точно воспроизвести семантику PIC-полей.
  • Интеграции с API: современные системы часто получают данные в JSON или XML, но внутренняя логика может требовать преобразования в COBOL-форматы для совместимости с существующими модулями.
  • Обучения и стандартизации: понимание PIC помогает разработчикам мыслить в терминах точности, масштаба и формата — навыки, полезные даже при работе с Python, Java или SQL.

Многие современные языки заимствовали идеи, заложенные в COBOL: например, концепция decimal в Python или BigDecimal в Java напрямую отражает потребность в точных десятичных вычислениях, которую COBOL решал с самого начала.